home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet bezpieczenstwa
/
mini Pentoo LiveCD 2006.1
/
mpentoo-2006.1.iso
/
livecd.squashfs
/
usr
/
lib
/
gkismet
/
Locator.pm
< prev
next >
Wrap
Text File
|
2005-10-20
|
14KB
|
513 lines
#!/usr/bin/perl -w
#
# $Id: Locator.pm,v 1.16 2003/08/04 04:57:02 solovam Exp $
#
# This file is a part of gkismet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# Locator class
#
package Locator;
use Gnome;
use Math::Trig;
use Math::Complex;
use Misc;
use BssConnObserver;
use DrawingArea;
use strict;
@Locator::ISA = qw(BssConnObserver);
my $minHeadingDist = 10;
my $compassWidth = 400;
my $compassHeight = 60;
my @currCoordLabels = ('Longitude', 'Latitude', 'Bearing', 'Fix');
my @netCoordLabels = ('Longitude', 'Latitude', 'Distance', 'Bearing');
#
# Constructor
#
sub new
{
my $type = shift;
my $self = new BssConnObserver(@_);
bless $self, $type;
# Check if there are GPS data
if(!defined $self->{'connection'}->getGps()->{'lat'} || $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'aggpoints'} == 0)
{
my $messagebox = new Gnome::MessageBox("No GPS data for network bssid: " . $self->{'bssid'}, 'error', 'Button_Ok');
$messagebox->run_and_close();
return undef;
}
# Init vars
$self->{'mode'} = 'averageCenter';
$self->{'lastLat'} = $self->{'connection'}->getGps()->{'lat'};
$self->{'lastLon'} = $self->{'connection'}->getGps()->{'lon'};
$self->{'headingAngle'} = 0;
$self->recalculate();
# Window
my $window = new Gtk::Window('dialog');
$window->signal_connect("delete_event", sub {$self->deleteHandler()});
$window->set_title('Network locator ' . $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'ssid'} . ' ' . $self->{'bssid'});
$self->{'window'} = $window;
# Drawing area
$self->{'drawingArea'} = new DrawingArea($compassWidth, $compassHeight, sub {$self->drawCompass()});
my $compassFrame = new Gtk::Frame(undef);
$compassFrame->add($self->{'drawingArea'}->getWidget());
my $fixed = new Gtk::Fixed();
$fixed->put($compassFrame, 0, 0);
my $compassHbox= new Gtk::HBox($true, 0);
$compassHbox->pack_start($fixed, $true, $false, 20);
# Labels
my $currCoordFrame = new Gtk::Frame(undef);
$self->{'currCoordFrame'} = $currCoordFrame;
$currCoordFrame->set_label('Current coordinates');
my $currCoordVbox = new Gtk::VBox($false, 5);
$currCoordFrame->add($currCoordVbox);
for my $l (@currCoordLabels)
{
my $hbox = new Gtk::HBox($false, 1);
my $nlabel = new Gtk::Label($l . ': ');
$nlabel->set_justify('left');
$hbox->pack_start($nlabel, $false, $false, 5);
my $vlabel = new Gtk::Label('');
$vlabel->set_justify('right');
$hbox->pack_end($vlabel, $false, $false, 5);
$currCoordVbox->pack_start($hbox, $false, $false, 3);
$self->{'currCoordLabel'}{$l} = $vlabel;
}
my $netCoordFrame = new Gtk::Frame(undef);
$self->{'netCoordFrame'} = $netCoordFrame;
$netCoordFrame->set_label('Average network center');
my $netCoordVbox = new Gtk::VBox($false, 5);
$netCoordFrame->add($netCoordVbox);
for my $l (@netCoordLabels)
{
my $hbox = new Gtk::HBox($false, 1);
my $nlabel = new Gtk::Label($l . ': ');
$nlabel->set_justify('left');
$hbox->pack_start($nlabel, $false, $false, 5);
my $vlabel = new Gtk::Label('');
$vlabel->set_justify('right');
$hbox->pack_end($vlabel, $false, $false, 5);
$netCoordVbox->pack_start($hbox, $false, $false, 3);
$self->{'netCoordLabel'}{$l} = $vlabel;
}
$self->updateLabels();
# Buttons
my $bbox = new Gtk::HButtonBox();
$bbox->set_layout('spread');
$bbox->set_spacing(5);
my $buttonClose = new Gtk::Button("Close");
$buttonClose->signal_connect( "clicked", sub {$self->closeClicked()});
$bbox->add($buttonClose);
my $buttonMode = new Gtk::Button("Mode");
$buttonMode->signal_connect( "clicked", sub {$self->modeClicked($buttonMode)});
$bbox->add($buttonMode);
# Assemble
my $hbox = new Gtk::HBox($true, 0);
$hbox->pack_start($currCoordFrame, $true, $true, 20);
$hbox->pack_start($netCoordFrame, $true, $true, 20);
my $vbox = new Gtk::VBox($false, 10);
$vbox->pack_start($compassHbox, $true, $false, 20);
$vbox->pack_start($hbox, $true, $false, 0);
$vbox->pack_start($bbox, $true, $false, 20);
$window->add($vbox);
$window->show_all();
# Start updates
$self->{'connection'}->addObserver($self);
return $self;
}
#
# Update labels
#
sub updateLabels
{
my $self = shift;
$self->{'currCoordLabel'}{'Latitude'}->set_text(sprintf "%.6f", $self->{'lat'});
$self->{'currCoordLabel'}{'Longitude'}->set_text(sprintf "%.6f", $self->{'lon'});
$self->{'currCoordLabel'}{'Bearing'}->set_text(sprintf "%.0f", $self->{'headingAngle'});
my $fix;
if($self->{'fix'} == -1)
{
$fix = 'No signal';
}
elsif($self->{'fix'} == 2)
{
$fix = '2D';
}
elsif($self->{'fix'} == 3)
{
$fix = '3D';
}
else
{
$fix = 'NONE';
}
$self->{'currCoordLabel'}{'Fix'}->set_text($fix);
$self->{'netCoordLabel'}{'Latitude'}->set_text(sprintf "%.6f", $self->{'centerLat'});
$self->{'netCoordLabel'}{'Longitude'}->set_text(sprintf "%.6f", $self->{'centerLon'});
$self->{'netCoordLabel'}{'Bearing'}->set_text(sprintf "%.0f", $self->{'centerAngle'});
my $dist = '';
if($self->{'gKismetApplication'}->{'preferences'}->getPref('units') eq 'metric')
{
if($self->{'dist'} < 1000)
{
$dist = sprintf("%.2fm", $self->{'dist'});
}
else
{
$dist = sprintf("%.3fkm", $self->{'dist'} / 1000);
}
}
else
{
if($self->{'dist'} / $mileFactor > 0.5)
{
$dist = sprintf("%.3fmi", $self->{'dist'} / $mileFactor);
}
else
{
$dist = sprintf("%.2fft", $self->{'dist'} / $footFactor);
}
}
$self->{'netCoordLabel'}{'Distance'}->set_text($dist);
}
#
# Change network locator mode
#
sub modeClicked
{
my $self = shift;
if($self->{'mode'} eq 'averageCenter')
{
$self->{'mode'} = 'strongestSignal';
$self->{'netCoordFrame'}->set_label('Strongest signal point');
}
elsif($self->{'mode'} eq 'strongestSignal')
{
$self->{'mode'} = 'averageCenter';
$self->{'netCoordFrame'}->set_label('Average network center');
}
$self->recalculate();
$self->drawCompass();
$self->updateLabels();
}
#
# Draw our compass into the pixmap
#
sub drawCompass
{
my $self = shift;
# Abbreviations
my $pixmap = $self->{'drawingArea'}->pixmap();
my $gtkDrawingArea = $self->{'drawingArea'}->gtkDrawingArea();
# Backdrop
$pixmap->draw_rectangle($gtkDrawingArea->style()->black_gc(), $true,
0, 0, $gtkDrawingArea->allocation()->[2], $gtkDrawingArea->allocation()->[3]);
# Middle horizontal white line
$pixmap->draw_line($gtkDrawingArea->style()->white_gc(), 0, $compassHeight/2, $compassWidth, $compassHeight/2);
# Red vertical line
$pixmap->draw_line($self->{'drawingArea'}->{'redGC'}, $compassWidth/2, 0, $compassWidth/2, $compassHeight);
# Digits on the gauge every 30 degrees
for(my $i = 0; $i < 12; $i++)
{
# Linear position of a mark
my $l = $self->deg2pix($i * 30);
# Draw a mark
$pixmap->draw_line($gtkDrawingArea->style()->white_gc(), $l, $compassHeight / 2 + 5, $l, $compassHeight / 2 - 5);
# Draw a label
$pixmap->draw_string($gtkDrawingArea->style()->font(), $gtkDrawingArea->style()->white_gc(),
# Shift text left by half of string length
$l - $self->{'drawingArea'}->stringWidth($i * 30) / 2,
# Shift text down from the middle by mark height (5) + padding (5) + string height
$compassHeight / 2 + 5 + $self->{'drawingArea'}->stringHeight($i * 30) + 5,
$i * 30);
}
# North, south, west, east letters. Pretty mach same as above
for my $i ([0, 'N'], [90, 'E'], [180, 'S'], [270, 'W'])
{
my $l = $self->deg2pix($i->[0]);
$pixmap->draw_string($gtkDrawingArea->style()->font(), $gtkDrawingArea->style()->white_gc(),
$l - $self->{'drawingArea'}->stringWidth($i->[1]) / 2,
$compassHeight / 2 - 5 - 5,
$i->[1]);
}
# Linear position of the center angle mark
my $l = $self->deg2pix($self->{'centerAngle'});
# A little triangle
my @triangle = ($l - 5, $compassHeight, $l, $compassHeight - 5, $l + 5, $compassHeight);
$pixmap->draw_polygon($self->{'drawingArea'}->{'redGC'}, $true, @triangle);
# Redraw us
$gtkDrawingArea->draw();
return;
}
#
# Translate degress into pixels on our compass scale
#
sub deg2pix
{
my $self = shift;
my $deg = shift;
my $l = ($deg - $self->{'headingAngle'}) * $compassWidth / 360 + $compassWidth * 3/2;
while ($l >= $compassWidth)
{
$l -= $compassWidth;
}
return $l;
}
#
# Handle an update from an observable
#
sub update
{
my $self = shift;
my $object = shift;
my $data = shift;
if($object->isa('Connection') && defined $self->{'connection'} && $self->{'connection'} eq $object)
{
if($data->{'changed'} eq 'gps' || ($data->{'changed'} eq 'network' && $data->{'bssid'} eq $self->{'bssid'}))
{
$self->recalculate();
$self->drawCompass();
$self->updateLabels();
}
}
}
#
# Repopulate coordinates, recalculate bearings and distance
#
sub recalculate
{
my $self = shift;
if($self->{'mode'} eq 'averageCenter')
{
$self->{'centerLat'} = $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'agglat'} /
$self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'aggpoints'};
$self->{'centerLon'} = $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'agglon'} /
$self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'aggpoints'};
}
elsif($self->{'mode'} eq 'strongestSignal')
{
$self->{'centerLat'} = $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'bestlat'};
$self->{'centerLon'} = $self->{'connection'}->getNetwork()->{$self->{'bssid'}}{'bestlon'};
}
$self->{'lat'} = $self->{'connection'}->getGps()->{'lat'};
$self->{'lon'} = $self->{'connection'}->getGps()->{'lon'};
$self->{'fix'} = $self->{'connection'}->getGps()->{'fix'};
$self->{'centerAngle'} = Locator->calcAngle($self->{'lat'}, $self->{'lon'}, $self->{'centerLat'}, $self->{'centerLon'});
$self->{'dist'} = Locator->earthDistance($self->{'lat'}, $self->{'lon'}, $self->{'centerLat'}, $self->{'centerLon'});
if(Locator->earthDistance($self->{'lastLat'}, $self->{'lastLon'}, $self->{'lat'}, $self->{'lon'}) >= $minHeadingDist)
{
$self->{'headingAngle'} = Locator->calcAngle($self->{'lastLat'}, $self->{'lastLon'}, $self->{'lat'}, $self->{'lon'});
$self->{'lastLat'} = $self->{'lat'};
$self->{'lastLon'} = $self->{'lon'};
}
}
#
# Get Gtk widget
#
sub getWidget
{
my $self = shift;
return $self->{'window'};
}
#
# Distance from the center of Earth in meters
#
# Taken (with some mods) from Kismet
#
sub calcRad
{
my $type = shift;
my $lat = shift;
$lat = deg2rad($lat);
# the radius of curvature of an ellipsoidal Earth in the plane of the
# meridian is given by
#
# R' = a * (1 - e^2) / (1 - e^2 * (sin(lat))^2)^(3/2)
#
# where a is the equatorial radius,
# b is the polar radius, and
# e is the eccentricity of the ellipsoid = sqrt(1 - b^2/a^2)
#
# a = 6378 km (3963 mi) Equatorial radius (surface to center distance)
# b = 6356.752 km (3950 mi) Polar radius (surface to center distance)
# e = 0.081082 Eccentricity
my $a = 6378.137;
my $e2 = 0.081082 * 0.081082;
my $sc = sin($lat);
my $x = $a * (1.0 - $e2);
my $z = 1.0 - $e2 * $sc * $sc;
my $y = Misc->pow($z, 1.5);
my $r = $x / $y;
$r = $r * 1000;
return $r;
}
#
# Distance between points in meters
#
# Taken (with some mods) from Kismet
#
sub earthDistance
{
my $type = shift;
my ($lat1, $lon1, $lat2, $lon2) = (@_);
my $x1 = Locator->calcRad($lat1) * cos(deg2rad($lon1)) * sin(deg2rad(90 - $lat1));
my $x2 = Locator->calcRad($lat2) * cos(deg2rad($lon2)) * sin(deg2rad(90 - $lat2));
my $y1 = Locator->calcRad($lat1) * sin(deg2rad($lon1)) * sin(deg2rad(90 - $lat1));
my $y2 = Locator->calcRad($lat2) * sin(deg2rad($lon2)) * sin(deg2rad(90 - $lat2));
my $z1 = Locator->calcRad($lat1) * cos(deg2rad(90 - $lat1));
my $z2 = Locator->calcRad($lat2) * cos(deg2rad(90 - $lat2));
my $a = acos(($x1 * $x2 + $y1 * $y2 + $z1 * $z2) / Misc->pow(Locator->calcRad(($lat1 + $lat2) / 2), 2));
my $dist = Locator->calcRad(($lat1 + $lat2) / 2) * $a;
if(ref($dist) eq 'Math::Complex')
{
return 0;
}
return $dist;
}
#
# Azimuth
#
# Taken (with some mods) from Kismet
#
sub calcAngle
{
my $type = shift;
my($lat1, $lon1, $lat2, $lon2) = (@_);
my $R = Locator->calcRad($lat2);
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
my $angle;
# we are moving along parallel
if($lat1 == $lat2)
{
# east
if($lon2 > $lon1)
{
$angle = pi/2;
}
# west
elsif($lon2 < $lon1)
{
$angle = 3 * pi / 2;
}
# not moving at all
else
{
return(0);
}
}
# we are moving along meridian
elsif($lon1 == $lon2)
{
# north
if($lat2 > $lat1)
{
$angle = 0;
}
# south
elsif($lat2 < $lat1)
{
$angle = pi;
}
# we never get here
else
{
return(undef);
}
}
# otherwise we calculate heading
else
{
my $tx = $R * cos($lat1) * ($lon2 - $lon1);
my $ty = $R * ($lat2 - $lat1);
$angle = atan($tx / $ty);
if ($ty < 0)
{
$angle += pi;
}
if($angle >= (2 * pi))
{
$angle -= 2 * pi;
}
if($angle < 0)
{
$angle += 2 * pi;
}
}
return rad2deg($angle);
}
1;